package org.zn.note.jdk.thread;

import org.zn.note.common.util.ThreadUtil;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * 1、Linux内核还在持续更新完善，进程和线程的实现模块也是内核完善任务之一。
 * 2、线程数受内存（JVM堆内存、线程Stack内存 。。。）、操作系统线程数影响。（主要是内存）
 * /proc/sys/kernel/threads-max    -Xms   -Xmx   -Xss
 * 32位Linux单进程最大4G （^2^32）（核心态1G、用户态3G），大概线程数 3W 吧
 * 64位理论上 2^64（几乎无限大）。实际可能是 2^36 （大概64G）
 * <p>
 * 线程的状态
 * 1、创建后，NEW
 * 2、start(), READY
 * 3、RUNNING
 * 4、wait() WAITING( wati(long) TIMED WAITING)
 * 5、notify  Runnable
 * 6、TERMINATED
 * <p>
 * {@link Object#wait()}   native，object.wait前要拿到object的锁，wait后会释放锁
 * {@link Object#notify()} native，object.notify前要拿到object的锁
 * {@link Thread#yield()}  native，不释放锁，线程让步，放弃当前CPU，等待分时间片，Runnable，不一定起效果
 * {@link Thread#sleep(long)} native，不释放锁
 * {@link Thread#join()} thread-2等thread-1执行完后，再执行，则需要：在 thread-2中，调用 thread-1.join。 实现原理：
 * 1、synchronized(thread-1)
 * 2、wait   用对象thread-1的Monitor，此时thread-1的锁释放了
 * 3、当 thread-1执行完毕后，会隐式的 notifyAll （java自己的机制）
 * <p>
 * {@link Runnable#run()}
 * {@link Callable#call()} 和Runnable比：① 有返回值 ② 可抛异常
 * <p>
 * {@link Executor} -> {@link ExecutorService} -> {@link ThreadPoolExecutor}
 * {@link Executor#execute(Runnable)}
 * {@link ExecutorService#execute(Runnable)}
 * {@link ExecutorService#submit(Callable)} --> {@link Future}
 * <p>
 * {@link ThreadPoolExecutor}
 * {@link ThreadPoolExecutor#ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue, ThreadFactory, RejectedExecutionHandler)}
 * {@link Executors#newFixedThreadPool(int)}
 * {@link Executors#newCachedThreadPool()}
 * {@link Executors#newSingleThreadExecutor()}
 * 关于ThreadPoolExecutor参数：
 * 【int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
 * BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler】
 * 1、优先起corePoolSize处理
 * 2、corePoolSize满了，放到队列
 * 3、corePoolSize满了，队列满了，起maximumPoolSize
 * 4、全都满了，{@link RejectedExecutionHandler#rejectedExecution(Runnable, ThreadPoolExecutor)}
 * 注意：
 * A、线程只要处理完了，就会去queue里拿；
 * B、另外只要线程起起来了，就不分是不是core了；
 * C、线程数超过core并空闲了keepAliveTime后会释放，最后保证留下corePoolSize个线程，但并不一定是最开始corePoolSize阶段创建那些线程。
 * 前面参数都说了，就剩下{@link ThreadFactory#newThread(Runnable)}，默认实现：
 * {@link Executors#defaultThreadFactory()}
 * <p>
 * 关于 Thread interrupt
 * {@link Thread#interrupt()}，实例方法，在不同情况下，表现不同
 * 1、阻塞1：清空标志位，线程收到InterruptedException
 * 阻塞自：Object.wait、Thread.join、Thread.sleep
 * clear thread interrupt status (flag=false)，and thread receive an InterruptedException
 * 2、阻塞2：关channel，设置标志位，线程收到ClosedByInterruptException
 * 阻塞自：{@link java.nio.channels.InterruptibleChannel}
 * close this channel，set thread interrupt status(flag=true)，and thread receive an ClosedByInterruptException
 * 3、非阻塞：如果thread正在运行，仅设置标志位(flag=true)，至于线程是否获取标志位以及获取后做什么，都取决于程序实现了
 * 线程获取标志位的方法：
 * 内部基础方法 private native boolean isInterrupted(boolean ClearInterrupted); 返回标志位，并指定是否清空标志位
 * {@link Thread#interrupted()} // 类静态方法，currentThread().isInterrupted(true);  获取标志位，并清空（flag = false）
 * {@link Thread#isInterrupted()} // 实例方法，isInterrupted(false); 获取标志位，并不清空
 */
public class MyThread {

    private static final Object lock = new Object();

    // wait 释放锁，notify 不释放锁，两者都需要获取 object的锁
    // 这也可以理解，如果wait后不释放锁，那么外面获取不到锁，又怎么notify呢
    private void testWait() {
        new Thread(new TaskWait()).start();
        ThreadUtil.sleep(TimeUnit.SECONDS, 1L);
        synchronized (lock) {
            lock.notify();
            ThreadUtil.sleep(TimeUnit.SECONDS, 1L);
            System.out.println("testWait");
        }
    }

    private static class TaskWait implements Runnable {
        @Override
        public void run() {
            try {
                synchronized (lock) {
                    System.out.println("TaskWait Start.");
                    lock.wait(); // object.wait 必须先获取 object 的锁

                    // 因为wait释放锁，这里是为了测试一下，wait继续执行时，是否会又拿到锁
                    // 结果是：wait继续执行，因为还是在同步代码块里，所以还是要获取锁才能往下执行！
                    new Thread(() -> {
                        synchronized (lock) { // 也就是这块不会先执行，要等外面的代码块释放lock
                            System.out.printf("%s: get lock\n", Thread.currentThread().getName());
                        }
                    }, "NEW-THREAD").start();
                    TimeUnit.SECONDS.sleep(5);
                    System.out.println("TaskWait End.");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    // 为什么起名 join呢
    // 就是将本来并行的Thread串行了，就好像是合并了
    private void testJoin() {
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName());
                    ThreadUtil.sleep(TimeUnit.SECONDS, 1L);
                }
            }
        }, "Thread-1");
        thread1.start();
        Thread.currentThread().setName("Thread-Main");
        System.out.println(Thread.currentThread().getName());
        try {
//            thread1.join(); // 直到 thread1 执行完毕，本线程继续执行
            thread1.join(1000L); // thread1执行完毕 或 定时到了，则本线程继续执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName());
    }

    // Thread.yield(); 不释放锁
    // Thread.sleep(); 不释放锁
    private void testLock() {
        new Thread(new TaskLock(), "Thread-1").start();
        new Thread(new TaskLock(), "Thread-2").start();
    }

    private static class TaskLock implements Runnable {
        @Override
        public void run() {
            synchronized (TaskLock.class) {
                while (true) {
                    System.out.println(Thread.currentThread().getName());
//                    Thread.yield(); // 不释放锁
                    try {
//                        TimeUnit.SECONDS.sleep(1); // 不释放锁
                        Thread.sleep(1000L); // 不释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    // 优先级可以设置为 1-10，不同操作系统支持的不同，不建议直接写数字
    private void testPriority() {
        int maxSize = 10000000;
        List list = new ArrayList();
        Thread threadMinPriority = new Thread(new TaskPriority(list, 1, maxSize));
        threadMinPriority.setPriority(Thread.MIN_PRIORITY);
        Thread threadNormPriority = new Thread(new TaskPriority(list, 2, maxSize));
        threadNormPriority.setPriority(Thread.NORM_PRIORITY);
        Thread threadMaxPriority = new Thread(new TaskPriority(list, 3, maxSize));
        threadMaxPriority.setPriority(Thread.MAX_PRIORITY);
        threadMinPriority.start();
        threadNormPriority.start();
        threadMaxPriority.start();
        while (list.size() < maxSize) {
            ThreadUtil.sleep(TimeUnit.SECONDS, 3L);
        }
        System.out.println(sumList(list));
    }

    // 不用与外部类交互
    private static class TaskPriority implements Runnable {
        private int threadNo;
        private List list;
        private int maxSize;

        TaskPriority(List list, int threadNo, int maxSize) {
            this.list = list;
            this.threadNo = threadNo;
            this.maxSize = maxSize;
        }

        @Override
        public void run() {
            while (list.size() < maxSize) {
                synchronized (Task.class) {
                    if (list.size() < maxSize) { // dubbo check
                        list.add(threadNo);
                    }
                }
            }
        }
    }

    private Map sumList(List list) {
        Map resultMap = new HashMap();
        for (Object o : list) {
            Object obj = resultMap.get(o);
            if (obj != null) {
                resultMap.put(o, ((Integer) obj + 1));
            } else {
                resultMap.put(o, 1);
            }
        }
        return resultMap;
    }

    /*
    java初始线程
[6] Monitor Ctrl-Break
[5] Attach Listener //添加事件
[4] Signal Dispatcher // 分发处理给 JVM 信号的线程
[3] Finalizer //调用对象 finalize 方法的线程
[2] Reference Handler //清除 reference 线程
[1] main // main 线程,程序入口
     */
    private static void baseThreads() {
        // 获取 Java 线程管理 MXBean
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        // 不需要获取同步的 monitor 和 synchronizer 信息，仅获取线程和线程堆栈信息
        ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
        // 遍历线程信息，仅打印线程 ID 和线程名称信息
        for (ThreadInfo threadInfo : threadInfos) {
            System.out.println("[" + threadInfo.getThreadId() + "] " + threadInfo.getThreadName());
        }
    }

    public static void main(String[] args) {
        new MyThread().testWait();
    }
}